package com.eugenema.windowGui.controller;

import com.eugenema.windowGui.MainApplication;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.svg.SVGGlyph;
import io.datafx.controller.ViewController;
import io.datafx.controller.flow.Flow;
import io.datafx.controller.flow.FlowHandler;
import io.datafx.controller.flow.context.FXMLViewFlowContext;
import io.datafx.controller.flow.context.ViewFlowContext;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.geometry.BoundingBox;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author Eugenema
 * @date 2023/3/27 22:02
 */
@ViewController(value = "/fxml/main.fxml", title = "首页")
public final class MainController {
    private static Logger logger = LoggerFactory.getLogger(MainController.class);

    /** 上下文 key：主界面的流 */
    public static final String CONTEXT_KEY_FLOW = "ContentFlow";
    /** 上下文 key：主界面的流控制器 */
    public static final String CONTEXT_KEY_FLOW_HANDLER = "ContentFlowHandler";
    /** 上下文 key：主内容区 */
    public static final String CONTEXT_KEY_MAIN_CONTENT = "mainContent";
    /** 上下文 key：侧边栏 */
    public static final String CONTEXT_KEY_SIDE_MENU = "sideMenuContent";
    /** 上下文 key：加载图 */
    public static final String CONTEXT_KEY_LOAD = "loadSpinner";
    /** 上下文 key：程序界面 */
    public static final String CONTEXT_KEY_HOME_CONTENT = "homeContent";

    /** 视图流上下文 */
    @FXMLViewFlowContext
    private ViewFlowContext context;
    /** 界面 */
    private Stage stage;
    /** 界面根元素 */
    @FXML
    private StackPane home;
    /** 标题栏 */
    @FXML
    private StackPane titleBar;
    /** 主内容区 */
    @FXML
    private StackPane mainContent;
    /** 侧边栏内容区 */
    @FXML
    private StackPane sideMenuContent;

    /** 最大化标志 */
    private BooleanProperty customMaximize = new SimpleBooleanProperty(false);
    private boolean maximized = false;
    /** 程序关闭提示框 */
    private Alert closeAlert;
    /** 关闭按钮 */
    private JFXButton btnClose;
    /** 最小化按钮 */
    private JFXButton btnMin;
    /** 最大化按钮 */
    private JFXButton btnMax;
    /** 记录最大化之前的还原窗口 */
    private BoundingBox originalBox;
    /** 最大化窗口 */
    private BoundingBox maximizedBox;
    /** 是否允许移动 */
    private boolean allowMove = false;
    /** 是否正在拖动窗口 */
    private boolean isDragging = false;
    /** 横坐标偏移量 */
    private double xOffset = 0;
    /** 纵坐标偏移量 */
    private double yOffset = 0;

    /**
     * 加载时初始化 fxml
     *
     * @author Eugenema
     * @date 2023/3/30 21:23
     **/
    @PostConstruct
    public void init() throws Exception {
        //将程序界面注册到上下文
        context.register(CONTEXT_KEY_HOME_CONTENT, home);

        //获取主界面
        stage = (Stage) context.getRegisteredObject(MainApplication.CONTEXT_KEY_STAGE);

        //创建首页详情实例流，并注册到上下文中，作为主内容界面的流和流控制器
        Flow homeDetailFlow = new Flow(HomeDetailController.class);
        final FlowHandler homeDetailFlowHandler = homeDetailFlow.createHandler(context);
        context.register(CONTEXT_KEY_FLOW_HANDLER, homeDetailFlowHandler);
        context.register(CONTEXT_KEY_FLOW, homeDetailFlow);

        //创建加载图实例流，并注册到上下文中
        Flow spinnerFlow = new Flow(SpinnerController.class);
        final FlowHandler spinnerFlowHandler = spinnerFlow.createHandler(context);
        context.register(CONTEXT_KEY_LOAD, spinnerFlowHandler);

        //设置默认加载内容及其出场动画，并将主内容区注册到上下文中
        mainContent.getChildren().setAll(homeDetailFlowHandler.start());
        context.register(CONTEXT_KEY_MAIN_CONTENT, mainContent);

        //设置侧边栏及其出场动画，并将侧边栏内容区注册到上下文中
        Flow sideMenuFlow = new Flow(SideMenuController.class);
        final FlowHandler sideMenuFlowHandler = sideMenuFlow.createHandler(context);
        sideMenuContent.getChildren().setAll(sideMenuFlowHandler.start());
        context.register(CONTEXT_KEY_SIDE_MENU, sideMenuContent);

        //初始化标题栏
        initializeTitleBar();

        //初始化程序关闭提示框
        closeAlert = new Alert(Alert.AlertType.CONFIRMATION);
        closeAlert.setTitle("警告");
        closeAlert.setHeaderText("当前程序正在运行，关闭可能导致错误，是否强制退出？");
        closeAlert.setOnShowing(event -> stage.setAlwaysOnTop(false));
        closeAlert.setOnCloseRequest(event -> stage.setAlwaysOnTop(true));
    }

    /**
     * 初始化标题栏
     * 增加最大化、最小化、关闭按钮
     *
     * @author Eugenema
     * @date 2023/4/9 22:35
     *
     **/
    private void initializeTitleBar() {
        HBox buttonsContainer = new HBox();
        buttonsContainer.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT,
                CornerRadii.EMPTY,
                Insets.EMPTY)));
        buttonsContainer.setPadding(new Insets(4));
        buttonsContainer.setAlignment(Pos.CENTER_RIGHT);

        //添加边框
        home.setBorder(new Border(new BorderStroke(Color.rgb(64, 158, 255, 0.3),
                BorderStrokeStyle.SOLID,
                CornerRadii.EMPTY,
                new BorderWidths(2, 2, 2, 2))));

        //添加事件处理
        //1、标题栏双击实现最大化及还原
        buttonsContainer.addEventHandler(MouseEvent.MOUSE_CLICKED, (mouseEvent) -> {
            if (mouseEvent.getClickCount() == 2) {
                btnMax.fire();
            }
        });
        //2、鼠标进来，允许移动
        titleBar.addEventHandler(MouseEvent.MOUSE_ENTERED, (enter) -> allowMove = true);
        //3、鼠标出去，若不是拖动着出去，则不允许移动
        titleBar.addEventHandler(MouseEvent.MOUSE_EXITED, (enter) -> {
            if (!isDragging) {
                allowMove = false;
            }
        });
        //5、鼠标释放时，不允许拖动
        home.addEventFilter(MouseEvent.MOUSE_RELEASED, (mouseEvent) -> isDragging = false);
        //6、拖动改变窗口大小
        titleBar.setOnMouseDragged((mouseEvent) -> handleDragEventOnDecoratorPane(mouseEvent));
        //7、鼠标按下时，初始化窗口坐标
        home.addEventHandler(MouseEvent.MOUSE_PRESSED, (mouseEvent) -> updateInitMouseValues(mouseEvent));
        titleBar.addEventHandler(MouseEvent.MOUSE_PRESSED, (mouseEvent) -> updateInitMouseValues(mouseEvent));

        //添加最大化、最小化、关闭按钮
        buttonsContainer.getChildren().addAll(initializeButton());
        buttonsContainer.setMinWidth(180);

        //添加到标题栏
        titleBar.getChildren().setAll(buttonsContainer);
    }

    /**
     * 初始化最大化、最小化、关闭按钮
     *
     * @author Eugenema
     * @date 2023/4/9 22:37
     *
     * @return java.util.List<com.jfoenix.controls.JFXButton>
     **/
    private List<JFXButton> initializeButton() {
        //设置图标样式
        SVGGlyph minus = new SVGGlyph(0,
                "MINUS",
                "M804.571 420.571v109.714q0 22.857-16 38.857t-38.857 16h-694.857q-22.857 0-38.857-16t-16-38.857v-109.714q0-22.857 16-38.857t38.857-16h694.857q22.857 0 38.857 16t16 38.857z",
                Color.BLACK);
        minus.setSize(12, 2);
        minus.setTranslateY(4);
        SVGGlyph resizeMax = new SVGGlyph(0,
                "RESIZE_MAX",
                "M726 810v-596h-428v596h428zM726 44q34 0 59 25t25 59v768q0 34-25 60t-59 26h-428q-34 0-59-26t-25-60v-768q0-34 25-60t59-26z",
                Color.BLACK);
        resizeMax.setSize(12, 12);
        SVGGlyph resizeMin = new SVGGlyph(0,
                "RESIZE_MIN",
                "M80.842 943.158v-377.264h565.894v377.264h-565.894zM0 404.21v619.79h727.578v-619.79h-727.578zM377.264 161.684h565.894v377.264h-134.736v80.842h215.578v-619.79h-727.578v323.37h80.842v-161.686z",
                Color.BLACK);
        resizeMin.setSize(12, 12);
        SVGGlyph close = new SVGGlyph(0,
                "CLOSE",
                "M810 274l-238 238 238 238-60 60-238-238-238 238-60-60 238-238-238-238 60-60 238 238 238-238z",
                Color.BLACK);
        close.setSize(12, 12);

        //新建按钮
        btnClose = new JFXButton();
        btnClose.getStyleClass().add("jfx-decorator-button");
        btnClose.setCursor(Cursor.HAND);
        btnClose.setOnAction((action) -> {
            //若程序正在运行，则弹出确认关闭窗口
            if (HomeDetailController.runFlag) {
                Optional<ButtonType> selected = closeAlert.showAndWait();
                //点击了取消按钮或关闭
                if (!selected.isPresent() || selected.get() == ButtonType.CANCEL) {
                    return;
                }
                //关闭线程池
                logger.info("强制结束程序");
            }

            //关闭之前先最小化
            stage.setIconified(true);
            //关闭窗口
            stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST));
        });
        btnClose.setGraphic(close);
        btnClose.setRipplerFill(Color.WHITE);

        btnMin = new JFXButton();
        btnMin.getStyleClass().add("jfx-decorator-button");
        btnMin.setCursor(Cursor.HAND);
        btnMin.setOnAction((action) -> stage.setIconified(true));
        btnMin.setGraphic(minus);
        btnMin.setRipplerFill(Color.WHITE);

        btnMax = new JFXButton();
        btnMax.getStyleClass().add("jfx-decorator-button");
        btnMax.setCursor(Cursor.HAND);
        btnMax.setRipplerFill(Color.WHITE);
        btnMax.setOnAction((action) -> maximize(resizeMin, resizeMax));
        btnMax.setGraphic(resizeMax);

        ArrayList<JFXButton> jfxButtons = new ArrayList<>();
        jfxButtons.add(btnMin);
        jfxButtons.add(btnMax);
        jfxButtons.add(btnClose);

        return jfxButtons;
    }

    /**
     * 最大化、还原事件
     *
     * @param resizeMin
     * @param resizeMax
     *
     * @author Eugenema
     * @date 2023/4/9 22:45
     *
     **/
    private void maximize(SVGGlyph resizeMin, SVGGlyph resizeMax) {
        if (!isCustomMaximize()) {
            stage.setMaximized(!stage.isMaximized());
            maximized = stage.isMaximized();
            if (stage.isMaximized()) {
                btnMax.setGraphic(resizeMin);
            } else {
                btnMax.setGraphic(resizeMax);
            }
        } else {
            if (!maximized) {
                originalBox = new BoundingBox(stage.getX(), stage.getY(), stage.getWidth(), stage.getHeight());
                Screen screen = Screen.getScreensForRectangle(stage.getX(),
                        stage.getY(),
                        stage.getWidth(),
                        stage.getHeight()).get(0);
                Rectangle2D bounds = screen.getVisualBounds();
                maximizedBox = new BoundingBox(bounds.getMinX(),
                        bounds.getMinY(),
                        bounds.getWidth(),
                        bounds.getHeight());
                stage.setX(maximizedBox.getMinX());
                stage.setY(maximizedBox.getMinY());
                stage.setWidth(maximizedBox.getWidth());
                stage.setHeight(maximizedBox.getHeight());
                btnMax.setGraphic(resizeMin);
            } else {
                stage.setX(originalBox.getMinX());
                stage.setY(originalBox.getMinY());
                stage.setWidth(originalBox.getWidth());
                stage.setHeight(originalBox.getHeight());
                originalBox = null;
                btnMax.setGraphic(resizeMax);
            }
            maximized = !maximized;
        }
    }

    public final boolean isCustomMaximize() {
        return customMaximize.get();
    }

    /**
     * 展示边框拖动光标，即改变窗口大小的光标
     *
     * @author Eugenema
     * @date 2023/4/10 20:29
     *
     * @param mouseEvent
     *
     **/
    private void showDragCursorOnBorders(MouseEvent mouseEvent) {
        //全屏或最大化状态下不显示光标
        if (stage.isMaximized() || stage.isFullScreen() || maximized) {
            titleBar.setCursor(Cursor.DEFAULT);
            home.setCursor(Cursor.DEFAULT);
            return;
        }
        //窗口大小必须是可变的
        if (!stage.isResizable()) {
            return;
        }

        /** 鼠标 x 坐标 */
        double x = mouseEvent.getX();
        /** 鼠标 y 坐标 */
        double y = mouseEvent.getY();

        //窗口必须设置边框，以此保证鼠标只在边框位置更改指针样式
        if (home.getBorder() != null && home.getBorder().getStrokes().size() > 0) {
            double borderWidth = home.snappedLeftInset();
            if (isRightEdge(x)) {
                //判断斜边拉伸
                if (y < borderWidth) {
                    home.setCursor(Cursor.NE_RESIZE);
                } else if (y > home.getHeight() - borderWidth) {
                    home.setCursor(Cursor.SE_RESIZE);
                } else {
                    home.setCursor(Cursor.E_RESIZE);
                }
            } else if (isLeftEdge(x)) {
                //判断斜边拉伸
                if (y < borderWidth) {
                    home.setCursor(Cursor.NW_RESIZE);
                } else if (y > home.getHeight() - borderWidth) {
                    home.setCursor(Cursor.SW_RESIZE);
                } else {
                    home.setCursor(Cursor.W_RESIZE);
                }
            } else if (isTopEdge(y)) {
                //顶部区域有两个控件，需要都设置
                home.setCursor(Cursor.N_RESIZE);
            } else if (isBottomEdge(y)) {
                home.setCursor(Cursor.S_RESIZE);
            } else {
                home.setCursor(Cursor.DEFAULT);
            }
        }
    }

    /**
     * 是否是右边拖动窗口，改变大小
     *
     * @author Eugenema
     * @date 2023/4/10 20:39
     *
     * @param x 鼠标 x 坐标
     *
     * @return
     **/
    private boolean isRightEdge(double x) {
        final double width = home.getWidth();
        return x < width && x > width - home.snappedLeftInset();
    }

    /**
     * 是否是左边拖动窗口，改变大小
     *
     * @author Eugenema
     * @date 2023/4/10 20:39
     *
     * @param x 鼠标 x 坐标
     *
     * @return
     **/
    private boolean isLeftEdge(double x) {
        return x >= 0 && x < home.snappedLeftInset();
    }

    /**
     * 是否是上边拖动窗口，改变大小
     *
     * @author Eugenema
     * @date 2023/4/10 20:40
     *
     * @param y 鼠标 y 坐标
     *
     * @return
     **/
    private boolean isTopEdge(double y) {
        return y >= 0 && y < home.snappedLeftInset();
    }

    /**
     * 是否是下边拖动窗口，改变大小
     *
     * @author Eugenema
     * @date 2023/4/10 20:43
     *
     * @param y 鼠标 y 坐标
     *
     * @return
     **/
    private boolean isBottomEdge(double y) {
        final double height = home.getHeight();
        return y < height && y > height - home.snappedLeftInset();
    }

    /**
     * 鼠标拖动，移动窗口
     *
     * @author Eugenema
     * @date 2023/4/10 21:28
     *
     * @param mouseEvent
     *
     **/
    private void handleDragEventOnDecoratorPane(MouseEvent mouseEvent) {
        isDragging = true;

        //只有鼠标左键按下才能进行拖动
        if (!mouseEvent.isPrimaryButtonDown()) {
            return;
        }
        if (xOffset == -1 && yOffset == -1) {
            return;
        }

        /** 最大化时，不能拖动 */
        if (mouseEvent.isStillSincePress() || stage.isMaximized() || maximized) {
            return;
        }

        //根据当前鼠标的样式，确定窗口是该上下改变大小，还是左右改变大小，或者是斜对角改变大小
        if (allowMove) {
            stage.setX(mouseEvent.getScreenX() - xOffset);
            stage.setY(mouseEvent.getScreenY() - yOffset);
            mouseEvent.consume();
        }
    }

    /**
     * 初始化界面坐标及宽高度
     *
     * @author 马寅广
     * @date 2023/4/11 9:08
     *
     * @param mouseEvent
     *
     **/
    private void updateInitMouseValues(MouseEvent mouseEvent) {
        xOffset = mouseEvent.getSceneX();
        yOffset = mouseEvent.getSceneY();
    }

    /**
     * 切换节点是否可用
     *
     * @author 马寅广
     * @date 2023/11/3 16:08
     *
     * @param flag 标志
     *
     **/
    public static void switchNode(boolean flag, Node... nodes) {
        //恢复/禁用
        for (Node node : nodes) {
            if (flag) {
                node.setMouseTransparent(false);
                node.getStyleClass().remove("disabled");
            } else {
                node.setMouseTransparent(true);
                node.getStyleClass().add("disabled");
            }
        }
    }
}
